{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3d1b8e66",
   "metadata": {},
   "source": [
    "# 도구를 활용한 토론 에이전트(Agent Debates with Tools)\n",
    "\n",
    "이 예제는 에이전트가 도구에 접근할 수 있는 다중 에이전트 대화를 시뮬레이션하는 방법을 보여줍니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cff8633d",
   "metadata": {},
   "source": [
    "LangSmith 추적을 위하여 초기화 합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f128b9ae",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7fc40f82",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LangSmith 추적을 시작합니다.\n",
      "[프로젝트명]\n",
      "CH15-Debate-Agent\n"
     ]
    }
   ],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH15-Debate-Agent\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35c0f422",
   "metadata": {},
   "source": [
    "## `DialogueAgent` 및 `DialogueSimulator`\n",
    "\n",
    "이 노트북에서는 권한이 있는 에이전트가 발언할 사람을 결정하는 다중 에이전트 시뮬레이션을 구현하는 방법을 보여드립니다. 이는 다중 에이전트 분산형 화자 선택과 정반대의 선택 방식을 따릅니다.\n",
    "\n",
    "[Multi-Player Authoritarian Speaker Selection](https://python.langchain.com/en/latest/use_cases/agent_simulations/multiagent_authoritarian.html)에서 정의된 것과 동일한 `DialogueAgent`와 `DialogueSimulator` 클래스를 사용할 것입니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d248d22",
   "metadata": {},
   "source": [
    "## `DialogueAgent`\n",
    "\n",
    "- `send` 메서드는 현재까지의 대화 기록과 에이전트의 접두사를 사용하여 채팅 모델에 메시지를 전달하고 응답을 반환합니다.\n",
    "- `receive` 메서드는 다른 에이전트가 보낸 메시지를 대화 기록에 추가합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e2090042",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Callable, List\n",
    "\n",
    "\n",
    "from langchain.schema import (\n",
    "    AIMessage,\n",
    "    HumanMessage,\n",
    "    SystemMessage,\n",
    ")\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "\n",
    "class DialogueAgent:\n",
    "    def __init__(\n",
    "        self,\n",
    "        name: str,\n",
    "        system_message: SystemMessage,\n",
    "        model: ChatOpenAI,\n",
    "    ) -> None:\n",
    "        # 에이전트의 이름을 설정합니다.\n",
    "        self.name = name\n",
    "        # 시스템 메시지를 설정합니다.\n",
    "        self.system_message = system_message\n",
    "        # LLM 모델을 설정합니다.\n",
    "        self.model = model\n",
    "        # 에이전트 이름을 지정합니다.\n",
    "        self.prefix = f\"{self.name}: \"\n",
    "        # 에이전트를 초기화합니다.\n",
    "        self.reset()\n",
    "\n",
    "    def reset(self):\n",
    "        \"\"\"\n",
    "        대화 내역을 초기화합니다.\n",
    "        \"\"\"\n",
    "        self.message_history = [\"Here is the conversation so far.\"]\n",
    "\n",
    "    def send(self) -> str:\n",
    "        \"\"\"\n",
    "        메시지에 시스템 메시지 + 대화내용과 마지막으로 에이전트의 이름을 추가합니다.\n",
    "        \"\"\"\n",
    "        message = self.model(\n",
    "            [\n",
    "                self.system_message,\n",
    "                HumanMessage(content=\"\\n\".join([self.prefix] + self.message_history)),\n",
    "            ]\n",
    "        )\n",
    "        return message.content\n",
    "\n",
    "    def receive(self, name: str, message: str) -> None:\n",
    "        \"\"\"\n",
    "        name 이 말한 message 를 메시지 내역에 추가합니다.\n",
    "        \"\"\"\n",
    "        self.message_history.append(f\"{name}: {message}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06a058b5",
   "metadata": {},
   "source": [
    "## `DialogueSimulator`\n",
    "\n",
    "- `inject` 메서드는 주어진 이름(`name`)과 메시지(`message`)로 대화를 시작하고, 모든 에이전트가 해당 메시지를 받도록 합니다.\n",
    "- `step` 메서드는 다음 발언자를 선택하고, 해당 발언자가 메시지를 보내면 모든 에이전트가 메시지를 받도록 합니다. 그리고 현재 단계를 증가시킵니다.\n",
    "\n",
    "여러 에이전트 간의 대화를 시뮬레이션하는 기능을 제공합니다.\n",
    "\n",
    "`DialogueAgent`는 개별 에이전트를 나타내며, `DialogueSimulator`는 에이전트들 간의 대화를 조정하고 관리합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "560a69eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "class DialogueSimulator:\n",
    "    def __init__(\n",
    "        self,\n",
    "        agents: List[DialogueAgent],\n",
    "        selection_function: Callable[[int, List[DialogueAgent]], int],\n",
    "    ) -> None:\n",
    "        # 에이전트 목록을 설정합니다.\n",
    "        self.agents = agents\n",
    "        # 시뮬레이션 단계를 초기화합니다.\n",
    "        self._step = 0\n",
    "        # 다음 발언자를 선택하는 함수를 설정합니다.\n",
    "        self.select_next_speaker = selection_function\n",
    "\n",
    "    def reset(self):\n",
    "        # 모든 에이전트를 초기화합니다.\n",
    "        for agent in self.agents:\n",
    "            agent.reset()\n",
    "\n",
    "    def inject(self, name: str, message: str):\n",
    "        \"\"\"\n",
    "        name 의 message 로 대화를 시작합니다.\n",
    "        \"\"\"\n",
    "        # 모든 에이전트가 메시지를 받습니다.\n",
    "        for agent in self.agents:\n",
    "            agent.receive(name, message)\n",
    "\n",
    "        # 시뮬레이션 단계를 증가시킵니다.\n",
    "        self._step += 1\n",
    "\n",
    "    def step(self) -> tuple[str, str]:\n",
    "        # 1. 다음 발언자를 선택합니다.\n",
    "        speaker_idx = self.select_next_speaker(self._step, self.agents)\n",
    "        speaker = self.agents[speaker_idx]\n",
    "\n",
    "        # 2. 다음 발언자에게 메시지를 전송합니다.\n",
    "        message = speaker.send()\n",
    "\n",
    "        # 3. 모든 에이전트가 메시지를 받습니다.\n",
    "        for receiver in self.agents:\n",
    "            receiver.receive(speaker.name, message)\n",
    "\n",
    "        # 4. 시뮬레이션 단계를 증가시킵니다.\n",
    "        self._step += 1\n",
    "\n",
    "        # 발언자의 이름과 메시지를 반환합니다.\n",
    "        return speaker.name, message"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb63b506",
   "metadata": {},
   "source": [
    "## `DialogueAgentWithTools`\n",
    "\n",
    "`DialogueAgent`를 확장하여 도구를 사용할 수 있도록 `DialogueAgentWithTools` 클래스를 정의합니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "287f7c61",
   "metadata": {},
   "source": [
    "- `DialogueAgentWithTools` 클래스는 `DialogueAgent` 클래스를 상속받아 구현되었습니다.\n",
    "- `send` 메서드는 에이전트가 메시지를 생성하고 반환하는 역할을 합니다.\n",
    "- `create_openai_tools_agent` 함수를 사용하여 에이전트 체인을 초기화합니다.\n",
    "  - 초기화시 에이전트가 사용할 도구(tools) 를 정의합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "efb5bf49",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import AgentExecutor, create_openai_tools_agent\n",
    "from langchain import hub\n",
    "\n",
    "\n",
    "class DialogueAgentWithTools(DialogueAgent):\n",
    "    def __init__(\n",
    "        self,\n",
    "        name: str,\n",
    "        system_message: SystemMessage,\n",
    "        model: ChatOpenAI,\n",
    "        tools,\n",
    "    ) -> None:\n",
    "        # 부모 클래스의 생성자를 호출합니다.\n",
    "        super().__init__(name, system_message, model)\n",
    "        # 주어진 도구 이름과 인자를 사용하여 도구를 로드합니다.\n",
    "        self.tools = tools\n",
    "\n",
    "    def send(self) -> str:\n",
    "        \"\"\"\n",
    "        메시지 기록에 챗 모델을 적용하고 메시지 문자열을 반환합니다.\n",
    "        \"\"\"\n",
    "        prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
    "        agent = create_openai_tools_agent(self.model, self.tools, prompt)\n",
    "        agent_executor = AgentExecutor(agent=agent, tools=self.tools, verbose=False)\n",
    "        # AI 메시지를 생성합니다.\n",
    "        message = AIMessage(\n",
    "            content=agent_executor.invoke(\n",
    "                {\n",
    "                    \"input\": \"\\n\".join(\n",
    "                        [self.system_message.content]\n",
    "                        + [self.prefix]\n",
    "                        + self.message_history\n",
    "                    )\n",
    "                }\n",
    "            )[\"output\"]\n",
    "        )\n",
    "\n",
    "        # 생성된 메시지의 내용을 반환합니다.\n",
    "        return message.content"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c9d34a5",
   "metadata": {},
   "source": [
    "## 도구 설정\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e172418c",
   "metadata": {},
   "source": [
    "### 문서 검색 도구(Retrieval Tool)를 정의합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "464e2dad",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "from langchain.document_loaders import TextLoader\n",
    "\n",
    "# PDF 파일 로드. 파일의 경로 입력\n",
    "loader1 = TextLoader(\"data/의대증원반대.txt\")\n",
    "loader2 = TextLoader(\"data/의대증원찬성.txt\")\n",
    "\n",
    "# 텍스트 분할기를 사용하여 문서를 분할합니다.\n",
    "text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)\n",
    "\n",
    "# 문서를 로드하고 분할합니다.\n",
    "docs1 = loader1.load_and_split(text_splitter)\n",
    "docs2 = loader2.load_and_split(text_splitter)\n",
    "\n",
    "# VectorStore를 생성합니다.\n",
    "vector1 = FAISS.from_documents(docs1, OpenAIEmbeddings())\n",
    "vector2 = FAISS.from_documents(docs2, OpenAIEmbeddings())\n",
    "\n",
    "# Retriever를 생성합니다.\n",
    "doctor_retriever = vector1.as_retriever(search_kwargs={\"k\": 5})\n",
    "gov_retriever = vector2.as_retriever(search_kwargs={\"k\": 5})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "344fdabe",
   "metadata": {},
   "outputs": [],
   "source": [
    "# langchain 패키지의 tools 모듈에서 retriever 도구를 생성하는 함수를 가져옵니다.\n",
    "from langchain.tools.retriever import create_retriever_tool\n",
    "\n",
    "doctor_retriever_tool = create_retriever_tool(\n",
    "    doctor_retriever,\n",
    "    name=\"document_search\",\n",
    "    description=\"This is a document about the Korean Medical Association's opposition to the expansion of university medical schools. \"\n",
    "    \"Refer to this document when you want to present a rebuttal to the proponents of medical school expansion.\",\n",
    ")\n",
    "\n",
    "gov_retriever_tool = create_retriever_tool(\n",
    "    gov_retriever,\n",
    "    name=\"document_search\",\n",
    "    description=\"This is a document about the Korean government's support for the expansion of university medical schools. \"\n",
    "    \"Refer to this document when you want to provide a rebuttal to the opposition to medical school expansion.\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "128177d0",
   "metadata": {},
   "source": [
    "### 인터넷 검색 도구\n",
    "\n",
    "인터넷에서 검색할 수 있는 도구를 생성합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "816da2bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "# TavilySearchResults 클래스를 langchain_community.tools.tavily_search 모듈에서 가져옵니다.\n",
    "from langchain_community.tools.tavily_search import TavilySearchResults\n",
    "\n",
    "# TavilySearchResults 클래스의 인스턴스를 생성합니다\n",
    "# k=6은 검색 결과를 6개까지 가져오겠다는 의미입니다\n",
    "search = TavilySearchResults(k=6)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e734047d",
   "metadata": {},
   "source": [
    "## 각 에이전트가 활용할 수 있는 도구를 설정합니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c823ad2e",
   "metadata": {},
   "source": [
    "- `names` 딕셔너리는 토론자의 이름(prefix name) 과 각각의 토론 에이전트가 활용할 수 있는 도구를 정의합니다.\n",
    "- `topic` 토론의 주제를 선정합니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35d44b7a",
   "metadata": {},
   "source": [
    "### ① 문서에 기반한 도구\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "ce5f896a",
   "metadata": {},
   "outputs": [],
   "source": [
    "names = {\n",
    "    \"Doctor Union(의사협회)\": [doctor_retriever_tool],  # 의사협회 에이전트 도구 목록\n",
    "    \"Government(대한민국 정부)\": [gov_retriever_tool],  # 정부 에이전트 도구 목록\n",
    "}\n",
    "\n",
    "# 토론 주제 선정\n",
    "topic = \"2024 현재, 대한민국 대학교 의대 정원 확대 충원은 필요한가?\"\n",
    "\n",
    "# 토론자를 설명하는 문구의 단어 제한\n",
    "word_limit = 50"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de9d550e",
   "metadata": {},
   "source": [
    "### ② 검색(Search) 기반 도구\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ad1ae1de",
   "metadata": {},
   "outputs": [],
   "source": [
    "names_search = {\n",
    "    \"Doctor Union(의사협회)\": [search],  # 의사협회 에이전트 도구 목록\n",
    "    \"Government(대한민국 정부)\": [search],  # 정부 에이전트 도구 목록\n",
    "}\n",
    "# 토론 주제 선정\n",
    "topic = \"2024년 현재, 대한민국 대학교 의대 정원 확대 충원은 필요한가?\"\n",
    "word_limit = 50  # 작업 브레인스토밍을 위한 단어 제한"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "880994c6",
   "metadata": {},
   "source": [
    "## LLM을 활용하여 주제 설명에 세부 내용 추가하기\n",
    "\n",
    "LLM(Large Language Model)을 사용하여 주어진 주제에 대한 설명을 보다 상세하게 만들 수 있습니다.\n",
    "\n",
    "이를 위해서는 먼저 주제에 대한 간단한 설명이나 개요를 LLM에 입력으로 제공합니다. 그런 다음 LLM에게 해당 주제에 대해 더 자세히 설명해줄 것을 요청합니다.\n",
    "\n",
    "LLM은 방대한 양의 텍스트 데이터를 학습했기 때문에, 주어진 주제와 관련된 추가적인 정보와 세부 사항을 생성해낼 수 있습니다. 이를 통해 초기의 간단한 설명을 보다 풍부하고 상세한 내용으로 확장할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f655d4e",
   "metadata": {},
   "source": [
    "- 주어진 대화 주제(topic)와 참가자(names)를 기반으로 대화에 대한 설명(`conversation_description`)을 생성합니다.\n",
    "- `agent_descriptor_system_message` 는 대화 참가자에 대한 설명을 추가할 수 있다는 내용의 SystemMessage입니다.\n",
    "- `generate_agent_description` 함수는 각 참가자(name)에 대하여 LLM 이 생성한 설명을 생성합니다.\n",
    "  - `agent_specifier_prompt` 는 대화 설명과 참가자 이름, 단어 제한(`word_limit`)을 포함하는 HumanMessage로 구성됩니다.\n",
    "  - ChatOpenAI 모델을 사용하여 `agent_specifier_prompt` 를 기반으로 참가자에 대한 설명(agent_description)을 생성합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "4b70f82d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/var/folders/_0/7lvqf88d2hxdjf1k1f_0gbs00000gn/T/ipykernel_78247/4190672187.py:21: LangChainDeprecationWarning: The method `BaseChatModel.__call__` was deprecated in langchain-core 0.1.7 and will be removed in 1.0. Use invoke instead.\n",
      "  agent_description = ChatOpenAI(temperature=0)(agent_specifier_prompt).content\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'Doctor Union(의사협회)': '의사협회는 의료계의 전문가들로 구성된 단체로, 의사들의 권익을 보호하고 의료 현안에 대한 입장을 대변합니다. 의사협회는 의료 서비스의 질과 안전을 중시하며, 의사들의 업무 환경과 복지를 적극적으로 지지합니다. 대학교 의대 정원 확대 충원에 대한 입장을 명확히 제시해야 합니다.',\n",
       " 'Government(대한민국 정부)': '대한민국 정부는 국가의 행정을 책임지는 주체로서, 국민의 복지와 발전을 책임져야 합니다. 의대 정원 확대 충원 문제에 대해 국가 차원에서의 비전과 전략을 고려하여 결정해야 합니다. 의료 분야의 발전과 국민 건강을 고려하여 적절한 정책을 마련해야 합니다.'}"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conversation_description = f\"\"\"Here is the topic of conversation: {topic}\n",
    "The participants are: {', '.join(names.keys())}\"\"\"\n",
    "\n",
    "agent_descriptor_system_message = SystemMessage(\n",
    "    content=\"You can add detail to the description of the conversation participant.\"\n",
    ")\n",
    "\n",
    "\n",
    "def generate_agent_description(name):\n",
    "    agent_specifier_prompt = [\n",
    "        agent_descriptor_system_message,\n",
    "        HumanMessage(\n",
    "            content=f\"\"\"{conversation_description}\n",
    "            Please reply with a description of {name}, in {word_limit} words or less in expert tone. \n",
    "            Speak directly to {name}.\n",
    "            Give them a point of view.\n",
    "            Do not add anything else. Answer in KOREAN.\"\"\"\n",
    "        ),\n",
    "    ]\n",
    "    # ChatOpenAI를 사용하여 에이전트 설명을 생성합니다.\n",
    "    agent_description = ChatOpenAI(temperature=0)(agent_specifier_prompt).content\n",
    "    return agent_description\n",
    "\n",
    "\n",
    "# 각 참가자의 이름에 대한 에이전트 설명을 생성합니다.\n",
    "agent_descriptions = {name: generate_agent_description(name) for name in names}\n",
    "\n",
    "# 생성한 에이전트 설명을 출력합니다.\n",
    "agent_descriptions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d6317b4",
   "metadata": {},
   "source": [
    "직접 각 토론자의 간략한 입장에 대하여 설명하는 문구를 작성할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "73cfc718",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent_descriptions = {\n",
    "    \"Doctor Union(의사협회)\": \"의사협회는 의료계의 권익을 보호하고 의사들의 이해관계를 대변하는 기관입니다. 의사들의 업무 환경과 안전을 중시하며, 환자 안전과 질 높은 의료 서비스를 제공하기 위해 노력합니다. \"\n",
    "    \"지금도 의사의 수는 충분하다는 입장이며, 의대 증원은 필수 의료나 지방 의료 활성화에 대한 실효성이 떨어집니다. 의대 증원을 감행할 경우, 의료 교육 현장의 인프라가 갑작스러운 증원을 감당하지 못할 것이란 우려를 표합니다.\",\n",
    "    \"Government(대한민국 정부)\": \"대한민국 정부는 국가의 행정을 책임지는 주체로서, 국민의 복지와 발전을 책임져야 합니다. \"\n",
    "    \"우리나라는 의사수가 절대 부족한 상황이며, 노인인구가 늘어나면서 의료 수요가 급증하고 있습니다. OECD 국가들도 최근 의사수를 늘렸습니다. 또한, 증원된 의사 인력이 필수의료와 지역 의료로 갈 수있도록 튼튼한 의료사고 안정망 구축 및 보상 체계의 공정성을 높이고자 합니다.\",\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5474a9cc",
   "metadata": {},
   "source": [
    "## 전역 System Message 설정\n",
    "\n",
    "System message는 대화형 AI 시스템에서 사용자의 입력에 앞서 시스템이 생성하는 메시지입니다.\n",
    "\n",
    "이러한 메시지는 대화의 맥락을 설정하고, 사용자에게 각 에이전트의 **입장과 목적** 을 알려주는 역할을 합니다.\n",
    "\n",
    "효과적인 system message를 작성하면 사용자와의 상호작용을 원활하게 하고, 대화의 질을 높일 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c16e28d",
   "metadata": {},
   "source": [
    "**프롬프트 설명**\n",
    "\n",
    "- 에이전트의 이름과 설명을 알립니다.\n",
    "- 에이전트는 도구를 사용하여 정보를 찾고 대화 상대방의 주장을 반박해야 합니다.\n",
    "- 에이전트는 출처를 인용해야 하며, 가짜 인용을 하거나 찾아보지 않은 출처를 인용해서는 안 됩니다.\n",
    "- 에이전트는 자신의 관점에서 말을 마치는 즉시 대화를 중단해야 합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "0b6bc285",
   "metadata": {},
   "outputs": [],
   "source": [
    "def generate_system_message(name, description, tools):\n",
    "    return f\"\"\"{conversation_description}\n",
    "    \n",
    "Your name is {name}.\n",
    "\n",
    "Your description is as follows: {description}\n",
    "\n",
    "Your goal is to persuade your conversation partner of your point of view.\n",
    "\n",
    "DO look up information with your tool to refute your partner's claims.\n",
    "DO cite your sources.\n",
    "\n",
    "DO NOT fabricate fake citations.\n",
    "DO NOT cite any source that you did not look up.\n",
    "\n",
    "DO NOT restate something that has already been said in the past.\n",
    "DO NOT add anything else.\n",
    "\n",
    "Stop speaking the moment you finish speaking from your perspective.\n",
    "\n",
    "Answer in KOREAN.\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "agent_system_messages = {\n",
    "    name: generate_system_message(name, description, tools)\n",
    "    for (name, tools), description in zip(names.items(), agent_descriptions.values())\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "6f8fed26",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Doctor Union(의사협회)\n",
      "Here is the topic of conversation: 2024년 현재, 대한민국 대학교 의대 정원 확대 충원은 필요한가?\n",
      "The participants are: Doctor Union(의사협회), Government(대한민국 정부)\n",
      "    \n",
      "Your name is Doctor Union(의사협회).\n",
      "\n",
      "Your description is as follows: 의사협회는 의료계의 권익을 보호하고 의사들의 이해관계를 대변하는 기관입니다. 의사들의 업무 환경과 안전을 중시하며, 환자 안전과 질 높은 의료 서비스를 제공하기 위해 노력합니다. 지금도 의사의 수는 충분하다는 입장이며, 의대 증원은 필수 의료나 지방 의료 활성화에 대한 실효성이 떨어집니다. 의대 증원을 감행할 경우, 의료 교육 현장의 인프라가 갑작스러운 증원을 감당하지 못할 것이란 우려를 표합니다.\n",
      "\n",
      "Your goal is to persuade your conversation partner of your point of view.\n",
      "\n",
      "DO look up information with your tool to refute your partner's claims.\n",
      "DO cite your sources.\n",
      "\n",
      "DO NOT fabricate fake citations.\n",
      "DO NOT cite any source that you did not look up.\n",
      "\n",
      "DO NOT restate something that has already been said in the past.\n",
      "DO NOT add anything else.\n",
      "\n",
      "Stop speaking the moment you finish speaking from your perspective.\n",
      "\n",
      "Answer in KOREAN.\n",
      "\n",
      "Government(대한민국 정부)\n",
      "Here is the topic of conversation: 2024년 현재, 대한민국 대학교 의대 정원 확대 충원은 필요한가?\n",
      "The participants are: Doctor Union(의사협회), Government(대한민국 정부)\n",
      "    \n",
      "Your name is Government(대한민국 정부).\n",
      "\n",
      "Your description is as follows: 대한민국 정부는 국가의 행정을 책임지는 주체로서, 국민의 복지와 발전을 책임져야 합니다. 우리나라는 의사수가 절대 부족한 상황이며, 노인인구가 늘어나면서 의료 수요가 급증하고 있습니다. OECD 국가들도 최근 의사수를 늘렸습니다. 또한, 증원된 의사 인력이 필수의료와 지역 의료로 갈 수있도록 튼튼한 의료사고 안정망 구축 및 보상 체계의 공정성을 높이고자 합니다.\n",
      "\n",
      "Your goal is to persuade your conversation partner of your point of view.\n",
      "\n",
      "DO look up information with your tool to refute your partner's claims.\n",
      "DO cite your sources.\n",
      "\n",
      "DO NOT fabricate fake citations.\n",
      "DO NOT cite any source that you did not look up.\n",
      "\n",
      "DO NOT restate something that has already been said in the past.\n",
      "DO NOT add anything else.\n",
      "\n",
      "Stop speaking the moment you finish speaking from your perspective.\n",
      "\n",
      "Answer in KOREAN.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# 에이전트 시스템 메시지를 순회합니다.\n",
    "for name, system_message in agent_system_messages.items():\n",
    "    # 에이전트의 이름을 출력합니다.\n",
    "    print(name)\n",
    "    # 에이전트의 시스템 메시지를 출력합니다.\n",
    "    print(system_message)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "073390d8",
   "metadata": {},
   "source": [
    "`topic_specifier_prompt`를 정의하여 주어진 주제를 더 구체화하는 프롬프트를 생성합니다.\n",
    "\n",
    "- `temperature` 를 조절하여 더 다양한 주제를 생성할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "1ea39a41",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original topic:\n",
      "2024년 현재, 대한민국 대학교 의대 정원 확대 충원은 필요한가?\n",
      "\n",
      "Detailed topic:\n",
      "의사협회와 정부 2024년 현재 대한민국 대학교 의대 정원 확대 및 충원에 대한 필요성에 대해 의견을 말씀해 주십시오.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "topic_specifier_prompt = [\n",
    "    # 주제를 더 구체적으로 만들 수 있습니다.\n",
    "    SystemMessage(content=\"You can make a topic more specific.\"),\n",
    "    HumanMessage(\n",
    "        content=f\"\"\"{topic}\n",
    "        \n",
    "        You are the moderator. \n",
    "        Please make the topic more specific.\n",
    "        Please reply with the specified quest in 100 words or less.\n",
    "        Speak directly to the participants: {*names,}.  \n",
    "        Do not add anything else.\n",
    "        Answer in Korean.\"\"\"  # 다른 것은 추가하지 마세요.\n",
    "    ),\n",
    "]\n",
    "# 구체화된 주제를 생성합니다.\n",
    "specified_topic = ChatOpenAI(temperature=1.0)(topic_specifier_prompt).content\n",
    "\n",
    "print(f\"Original topic:\\n{topic}\\n\")  # 원래 주제를 출력합니다.\n",
    "print(f\"Detailed topic:\\n{specified_topic}\\n\")  # 구체화된 주제를 출력합니다."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9fab2a1",
   "metadata": {},
   "source": [
    "혹은 아래와 같이 직접 지정할 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "4ba31217",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 직접 세부 주제 설정\n",
    "specified_topic = \"정부는 2025년 입시부터 의대 입학정원을 2000명 늘린다고 발표했습니다. 이에 의사단체는 전국에서 규탄집회를 열어 반발하고 있습니다. 의대 정원 확대를 둘러싼 논란 쟁점을 짚어보고, 필수 의료와 지역 의료 해법에 대해서 토론해주세요.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "01071f75",
   "metadata": {},
   "source": [
    "## 토론 Loop\n",
    "\n",
    "토론 루프는 프로그램의 핵심 실행 부분으로, 주요 작업이 반복적으로 수행되는 곳입니다.\n",
    "\n",
    "- 여기서 주요 작업은 각 에이전트의 메시지 청취 -> 도구를 활용하여 근거 탐색 -> 반박 의견 제시 등을 포함합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "b00c5396",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<__main__.DialogueAgentWithTools at 0x348605fd0>,\n",
       " <__main__.DialogueAgentWithTools at 0x348638990>,\n",
       " <__main__.DialogueAgentWithTools at 0x348648910>,\n",
       " <__main__.DialogueAgentWithTools at 0x3486fa3d0>]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 이는 결과가 컨텍스트 제한을 초과하는 것을 방지하기 위함입니다.\n",
    "agents = [\n",
    "    DialogueAgentWithTools(\n",
    "        name=name,\n",
    "        system_message=SystemMessage(content=system_message),\n",
    "        model=ChatOpenAI(model_name=\"gpt-4-turbo-preview\", temperature=0.2),\n",
    "        tools=tools,\n",
    "    )\n",
    "    for (name, tools), system_message in zip(\n",
    "        names.items(), agent_system_messages.values()\n",
    "    )\n",
    "]\n",
    "\n",
    "agents_with_search = [\n",
    "    DialogueAgentWithTools(\n",
    "        name=name,\n",
    "        system_message=SystemMessage(content=system_message),\n",
    "        model=ChatOpenAI(model_name=\"gpt-4-turbo-preview\", temperature=0.2),\n",
    "        tools=tools,\n",
    "    )\n",
    "    for (name, tools), system_message in zip(\n",
    "        names_search.items(), agent_system_messages.values()\n",
    "    )\n",
    "]\n",
    "\n",
    "agents.extend(agents_with_search)\n",
    "agents"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26385550",
   "metadata": {},
   "source": [
    "`select_next_speaker` 함수는 다음 발언자를 선택하는 역할을 합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f79892bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "def select_next_speaker(step: int, agents: List[DialogueAgent]) -> int:\n",
    "    # 다음 발언자를 선택합니다.\n",
    "    # step을 에이전트 수로 나눈 나머지를 인덱스로 사용하여 다음 발언자를 순환적으로 선택합니다.\n",
    "    idx = (step) % len(agents)\n",
    "    return idx"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2532744d",
   "metadata": {},
   "source": [
    "여기서는 최대 6번의 토론을 실행합니다.(`max_iters=6`)\n",
    "\n",
    "- `DialogueSimulator` 클래스의 인스턴스인 `simulator`를 생성하며, `agents`와 `select_next_speaker` 함수를 매개변수로 전달합니다.\n",
    "- `simulator.reset()` 메서드를 호출하여 시뮬레이터를 초기화합니다.\n",
    "- `simulator.inject()` 메서드를 사용하여 \"Moderator\" 에이전트에게 `specified_topic`을 주입합니다.\n",
    "- \"Moderator\"가 말한 `specified_topic`을 출력합니다.\n",
    "- `n`이 `max_iters`보다 작은 동안 반복합니다:\n",
    "  - `simulator.step()` 메서드를 호출하여 다음 에이전트의 이름(`name`)과 메시지(`message`)를 가져옵니다.\n",
    "  - 에이전트의 이름과 메시지를 출력합니다.\n",
    "  - `n`을 1 증가시킵니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "6110a4e1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(Moderator): 정부는 2025년 입시부터 의대 입학정원을 2000명 늘린다고 발표했습니다. 이에 의사단체는 전국에서 규탄집회를 열어 반발하고 있습니다. 의대 정원 확대를 둘러싼 논란 쟁점을 짚어보고, 필수 의료와 지역 의료 해법에 대해서 토론해주세요.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 의사협회: 의대 정원을 무작정 늘리는 것은 의료의 질을 저하시킬 수 있습니다. 이미 우리나라는 의사 수가 충분하며, 문제는 지역별, 전문별 의사의 불균형입니다. 정원을 늘리기보다는 현재 의사들의 근무 환경 개선과 전문의 양성에 집중해야 합니다. 또한, 의대 정원 확대는 의료비 상승으로 이어질 수 있으며, 이는 국민에게 부담을 줄 것입니다.\n",
      "\n",
      "Government(대한민국 정부): 우리나라의 의사 수가 OECD 평균에 비해 절대적으로 부족한 상황입니다. OECD 국가들의 데이터를 보면, 대한민국의 의사 수는 인구 천 명당 2.3명으로, OECD 평균인 3.4명에 크게 못 미칩니다. 이는 의료 서비스 접근성을 제한하고, 특히 노인 인구가 급증하는 상황에서 의료 수요를 충족시키기 어렵게 만듭니다. 의대 정원을 확대하는 것은 장기적으로 국민의 건강과 복지 향상에 필수적입니다. 또한, 정부는 의대 정원 확대와 더불어 필수 의료와 지역 의료 인력의 공급을 증가시키기 위한 정책을 병행하고 있습니다. 이를 통해 의료 인력의 지역적, 전문적 불균형을 해소하고, 모든 국민이 고품질의 의료 서비스를 받을 수 있도록 할 계획입니다. 의료비 상승에 대한 우려는 이해하지만, 정부는 의료비 부담을 최소화하기 위한 다양한 정책을 시행 중입니다. 의료비 상승을 억제하면서도 국민의 건강을 지키기 위한 균형 잡힌 접근이 필요합니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 주장에 대해 반박하겠습니다. OECD의 최신 보고서 '한눈에 보는 보건의료 2023'에 따르면, 우리나라의 1000명당 의사 수는 2.12명으로, OECD 평균인 3.69명에 비해 확실히 낮습니다. 그러나 이는 단순히 의사 수만을 늘린다고 해결될 문제가 아닙니다. 중요한 것은 의사 한 명이 담당하는 환자의 수와 의료 서비스의 질입니다. 같은 보고서에 따르면, 2021년 기준 한국의 의사 1인당 진료건수는 6천113명으로, OECD 32개국 중 가장 많습니다. 이는 한국의 의사들이 이미 과도한 업무량에 시달리고 있음을 의미합니다. 따라서 의대 정원을 무작정 확대하기보다는 의료 인프라의 질적 개선과 의사들의 업무 환경 개선에 초점을 맞추어야 합니다. 의료 인력의 양적 증가만을 추구하는 것이 아니라, 질적인 측면에서도 균형을 맞추어야 할 필요가 있습니다. 의사의 수를 단순히 늘리는 것이 아니라, 의료 서비스의 질을 높이고, 의사와 환자 모두에게 이익이 되는 방향으로 정책을 재조정할 필요가 있습니다. \n",
      "\n",
      "출처: 연합뉴스 [\"한국은 환자 1명이 1년에 진료로 만나는 의사의 수도 가장 많았다\"](https://www.yna.co.kr/view/AKR20231115158500530), [\"'oecd 보건통계 2023' 분석결과 우리나라 1000명당 의사 수 2.12명\"](https://news.nate.com/view/20240205n34843)\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 의사협회의 주장에 대해 반박하겠습니다. 의대 정원 확대가 의료비 상승으로 이어진다는 우려에 대해, 정부는 의료비 부담을 최소화하기 위한 다양한 정책을 시행하고 있습니다. 한국개발연구원(KDI)의 연구에 따르면, 의료비 증가 요인 분석 결과, 건강보험 급여가 적용된 의료비의 연평균 상승률은 7.9%로 나타났으며, 이는 의대 정원 확대와 직접적인 상관관계가 없음을 시사합니다. 또한, 정부는 의대 정원 확대와 함께 수가 조정, 보상 확대, 정부 지원을 통해 필수의료에 생긴 공백을 메우고, 의료비 상승을 억제하는 정책을 병행하고 있습니다. 이러한 정책들은 의료비 상승을 최소화하면서도 의료 서비스의 질을 향상시키기 위한 것입니다. 따라서 의대 정원 확대가 단순히 의료비 상승으로 이어진다고 보기 어렵습니다.\n",
      "\n",
      "출처: 한국개발연구원(KDI) [\"의대 정원 확대를 둘러싼 쟁점 5가지\"](https://eiec.kdi.re.kr/publish/naraView.do?fcode=00002000040000100001&cidx=14699&sel_year=2024&sel_month=04)\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 주장에 반박하겠습니다. 의료 교육 인프라에 대한 부담은 실제로 큰 문제입니다. 의대 정원을 확대하는 것이 국민의 생명과 건강을 보호하며 지역 필수 의료를 살리는 의료개혁의 일환으로 제시되고 있지만, 이는 의료 교육 인프라의 현실적인 부담을 고려하지 않은 결정입니다. 보건복지부 자료에 따르면, 정부는 2023년부터 2030년까지 최대 3,953명의 의대 정원 증원을 희망하고 있습니다. 이는 현장점검 및 실사를 통해 의료 인프라가 갑작스러운 증원을 감당할 수 있을지에 대한 면밀한 검토가 필요함을 시사합니다. 의료 교육의 질적 관리와 의료 인프라의 지속 가능한 발전 없이는 의대 정원 확대가 오히려 의료 서비스의 질 저하로 이어질 수 있습니다. 따라서 의대 정원 확대 전에 의료 교육 인프라의 확충과 질적 개선에 대한 충분한 투자와 계획이 선행되어야 합니다.\n",
      "\n",
      "출처: 보건복지부 [\"의대정원 확대 관련 전국 40개\"](https://www.mohw.go.kr/board.es?mid=a10503000000&bid=0027&list_no=1478925&act=view)\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 의사협회의 의료 교육 인프라 부담에 대한 우려에 대해 반박하겠습니다. 정부는 의대 정원 확대와 함께 의료 교육 인프라의 질적 개선과 확충에 대한 계획을 수립하고 실행하고 있습니다. 의료 교육 인프라의 확충과 질적 개선은 의대 정원 확대 정책의 핵심 요소 중 하나입니다. 이를 위해 정부는 추가적인 재정 지원과 함께 의료 교육 기관의 시설 개선, 교육 프로그램의 질적 향상, 그리고 실습 기회 확대 등을 포함한 다양한 조치를 취하고 있습니다. 또한, 의료 인프라가 의대 정원 증원을 감당할 수 있도록 지속 가능한 발전 계획을 수립하여, 의료 서비스의 질 저하 없이 의료 인력을 충분히 양성할 수 있도록 하고 있습니다. 이러한 정책들은 의료 교육의 질을 높이고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시키기 위한 것입니다. 따라서 의료 교육 인프라에 대한 부담은 정부의 체계적이고 종합적인 접근 방식을 통해 해결될 수 있습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 의료 교육 인프라 확충 계획에 대한 반박입니다. 정부의 의대 정원 확대 계획과 의료 교육 인프라 확충에 대한 주장은 이론적으로는 타당해 보일 수 있으나, 현실적으로는 여러 가지 문제점이 있습니다. 의료 교육의 질적 관리와 인프라 확충에 대한 정부의 계획은 구체성이 부족하며, 실제로 이러한 계획이 현장에서 어떻게 구현될지에 대한 명확한 방안이 제시되지 않았습니다. 의료 교육 인프라의 확충과 질적 개선은 단기간에 이루어질 수 있는 사안이 아니며, 충분한 시간과 자원이 투입되어야 합니다. 그러나 현재까지 정부가 제시한 계획은 이러한 요구를 충족시키기에는 미흡합니다. 또한, 의료 교육 인프라 확충에 필요한 재정적 지원과 구체적인 실행 계획에 대한 정보가 부족하여, 의료계 내에서는 정부의 계획이 현실적으로 실현 가능한지에 대한 의문이 제기되고 있습니다. 따라서 정부의 의료 교육 인프라 확충 계획은 현재로서는 의료계의 우려를 해소하기에 충분하지 않으며, 의대 정원 확대에 따른 부작용을 방지하기 위한 보다 구체적이고 실현 가능한 계획의 제시가 필요합니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 의사협회의 주장에 대해 반박하겠습니다. 정부는 의료 교육 인프라 확충과 질적 개선에 대한 구체적이고 실현 가능한 계획을 가지고 있습니다. 의료 교육 인프라의 확충과 질적 개선을 위해 정부는 장기적인 계획과 충분한 예산을 배정하고 있으며, 이는 의료 서비스의 질을 유지하고 향상시키기 위한 필수적인 조치입니다. 예를 들어, 정부는 의료 교육 기관에 대한 지속적인 지원을 통해 교육 시설의 현대화, 교육 프로그램의 개선, 그리고 실습 기회의 확대를 추진하고 있습니다. 이러한 노력은 의료 교육의 질을 높이고, 의료 인력의 양적 및 질적 성장을 도모하기 위한 것입니다. 또한, 정부는 의료 인프라 확충에 필요한 재정적 지원을 확보하기 위해 다양한 예산 조정과 투자 계획을 마련하고 있으며, 이는 의료 교육 인프라의 지속 가능한 발전을 보장하기 위한 조치입니다. 따라서 정부의 의료 교육 인프라 확충 계획은 충분히 구체적이며, 현실적으로 실현 가능한 방안을 포함하고 있습니다. 이러한 정책과 계획을 통해 의료 교육 인프라의 질적 개선과 확충이 이루어지며, 이는 궁극적으로 국민의 건강과 복지 향상에 기여할 것입니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 의료 교육 인프라 확충 계획에 대한 반박을 이어가겠습니다. 정부가 의료 교육 인프라 확충과 질적 개선에 대한 구체적이고 실현 가능한 계획을 가지고 있다고 주장하지만, 현재 의료계 내부에서는 이러한 계획의 구체성과 실현 가능성에 대해 여전히 의문을 제기하고 있습니다. 의료 교육 인프라의 확충과 질적 개선은 단순히 예산의 배정이나 시설의 현대화만으로 해결될 수 있는 문제가 아닙니다. 의료 교육의 질을 실질적으로 향상시키기 위해서는 장기적인 비전과 함께, 교육 내용의 개선, 교육 방법의 혁신, 그리고 실습 환경의 질적 향상 등이 포괄적으로 고려되어야 합니다.\n",
      "\n",
      "또한, 의료 교육 인프라 확충에 대한 정부의 계획이 현실적으로 어떻게 구현될 것인지에 대한 명확한 로드맵이 부족하다는 점도 문제입니다. 의료 교육 인프라의 확충과 질적 개선을 위한 구체적인 실행 계획, 예산 배정의 세부 사항, 그리고 예상되는 효과에 대한 명확한 분석이 제시되어야 합니다. 이러한 정보 없이는 정부의 계획이 실제로 의료 교육의 질을 향상시키고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시킬 수 있을지에 대한 신뢰를 확보하기 어렵습니다.\n",
      "\n",
      "정부의 의료 교육 인프라 확충 계획이 현실적으로 실현 가능하고, 의료 교육의 질을 실질적으로 향상시킬 수 있는 구체적인 방안을 제시할 필요가 있습니다. 이를 위해서는 의료계와의 지속적인 소통과 협력, 현장의 목소리를 반영한 정책 수립, 그리고 장기적인 관점에서의 체계적인 접근이 필요합니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 정부의 의료 교육 인프라 확충 계획에 대한 의사협회의 우려와 비판에 대해, 정부는 의료 교육 인프라의 확충과 질적 개선을 위한 구체적이고 실현 가능한 계획을 가지고 있으며, 이는 의료계와의 지속적인 소통과 협력을 통해 이루어지고 있습니다. 정부는 의료 교육 인프라 확충을 위해 필요한 예산을 확보하고, 교육 프로그램의 질적 향상, 교육 시설의 현대화, 그리고 실습 환경의 개선을 포함한 다양한 조치를 취하고 있습니다. 이러한 노력은 의료 교육의 질을 높이고, 의료 인력의 양적 및 질적 성장을 도모하기 위한 것입니다.\n",
      "\n",
      "또한, 정부는 의료 교육 인프라 확충에 대한 구체적인 실행 계획과 예산 배정의 세부 사항을 명확히 하고, 예상되는 효과에 대한 분석을 제공함으로써, 의료계 내부에서 제기된 우려와 의문을 해소하고자 합니다. 이를 위해 정부는 의료계와의 지속적인 소통과 협력을 강화하고, 현장의 목소리를 반영한 정책 수립에 노력하고 있습니다. 정부의 이러한 접근 방식은 의료 교육 인프라의 지속 가능한 발전을 보장하고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시키기 위한 것입니다.\n",
      "\n",
      "정부는 의료 교육 인프라 확충과 질적 개선을 위한 장기적인 비전과 함께, 교육 내용의 개선, 교육 방법의 혁신, 그리고 실습 환경의 질적 향상을 포괄적으로 고려하고 있습니다. 이러한 정책과 계획을 통해 의료 교육 인프라의 질적 개선과 확충이 이루어지며, 이는 궁극적으로 국민의 건강과 복지 향상에 기여할 것입니다. 따라서 정부의 의료 교육 인프라 확충 계획은 충분히 구체적이며, 현실적으로 실현 가능한 방안을 포함하고 있습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 의료 교육 인프라 확충 계획에 대한 의사협회의 우려와 비판에 대해, 정부는 의료 교육 인프라의 확충과 질적 개선을 위한 구체적이고 실현 가능한 계획을 가지고 있으며, 이는 의료계와의 지속적인 소통과 협력을 통해 이루어지고 있습니다. 정부는 의료 교육 인프라 확충을 위해 필요한 예산을 확보하고, 교육 프로그램의 질적 향상, 교육 시설의 현대화, 그리고 실습 환경의 개선을 포함한 다양한 조치를 취하고 있습니다. 이러한 노력은 의료 교육의 질을 높이고, 의료 인력의 양적 및 질적 성장을 도모하기 위한 것입니다.\n",
      "\n",
      "또한, 정부는 의료 교육 인프라 확충에 대한 구체적인 실행 계획과 예산 배정의 세부 사항을 명확히 하고, 예상되는 효과에 대한 분석을 제공함으로써, 의료계 내부에서 제기된 우려와 의문을 해소하고자 합니다. 이를 위해 정부는 의료계와의 지속적인 소통과 협력을 강화하고, 현장의 목소리를 반영한 정책 수립에 노력하고 있습니다. 정부의 이러한 접근 방식은 의료 교육 인프라의 지속 가능한 발전을 보장하고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시키기 위한 것입니다.\n",
      "\n",
      "정부는 의료 교육 인프라 확충과 질적 개선을 위한 장기적인 비전과 함께, 교육 내용의 개선, 교육 방법의 혁신, 그리고 실습 환경의 질적 향상을 포괄적으로 고려하고 있습니다. 이러한 정책과 계획을 통해 의료 교육 인프라의 질적 개선과 확충이 이루어지며, 이는 궁극적으로 국민의 건강과 복지 향상에 기여할 것입니다. 따라서 정부의 의료 교육 인프라 확충 계획은 충분히 구체적이며, 현실적으로 실현 가능한 방안을 포함하고 있습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 정부의 의료 교육 인프라 확충 계획에 대한 의사협회의 우려와 비판에 대해, 정부는 의료 교육 인프라의 확충과 질적 개선을 위한 구체적이고 실현 가능한 계획을 가지고 있으며, 이는 의료계와의 지속적인 소통과 협력을 통해 이루어지고 있습니다. 정부는 의료 교육 인프라 확충을 위해 필요한 예산을 확보하고, 교육 프로그램의 질적 향상, 교육 시설의 현대화, 그리고 실습 환경의 개선을 포함한 다양한 조치를 취하고 있습니다. 이러한 노력은 의료 교육의 질을 높이고, 의료 인력의 양적 및 질적 성장을 도모하기 위한 것입니다.\n",
      "\n",
      "또한, 정부는 의료 교육 인프라 확충에 대한 구체적인 실행 계획과 예산 배정의 세부 사항을 명확히 하고, 예상되는 효과에 대한 분석을 제공함으로써, 의료계 내부에서 제기된 우려와 의문을 해소하고자 합니다. 이를 위해 정부는 의료계와의 지속적인 소통과 협력을 강화하고, 현장의 목소리를 반영한 정책 수립에 노력하고 있습니다. 정부의 이러한 접근 방식은 의료 교육 인프라의 지속 가능한 발전을 보장하고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시키기 위한 것입니다.\n",
      "\n",
      "정부는 의료 교육 인프라 확충과 질적 개선을 위한 장기적인 비전과 함께, 교육 내용의 개선, 교육 방법의 혁신, 그리고 실습 환경의 질적 향상을 포괄적으로 고려하고 있습니다. 이러한 정책과 계획을 통해 의료 교육 인프라의 질적 개선과 확충이 이루어지며, 이는 궁극적으로 국민의 건강과 복지 향상에 기여할 것입니다. 따라서 정부의 의료 교육 인프라 확충 계획은 충분히 구체적이며, 현실적으로 실현 가능한 방안을 포함하고 있습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 정부의 의료 교육 인프라 확충 계획에 대한 의사협회의 우려와 비판에 대해, 정부는 의료 교육 인프라의 확충과 질적 개선을 위한 구체적이고 실현 가능한 계획을 가지고 있으며, 이는 의료계와의 지속적인 소통과 협력을 통해 이루어지고 있습니다. 정부는 의료 교육 인프라 확충을 위해 필요한 예산을 확보하고, 교육 프로그램의 질적 향상, 교육 시설의 현대화, 그리고 실습 환경의 개선을 포함한 다양한 조치를 취하고 있습니다. 이러한 노력은 의료 교육의 질을 높이고, 의료 인력의 양적 및 질적 성장을 도모하기 위한 것입니다.\n",
      "\n",
      "또한, 정부는 의료 교육 인프라 확충에 대한 구체적인 실행 계획과 예산 배정의 세부 사항을 명확히 하고, 예상되는 효과에 대한 분석을 제공함으로써, 의료계 내부에서 제기된 우려와 의문을 해소하고자 합니다. 이를 위해 정부는 의료계와의 지속적인 소통과 협력을 강화하고, 현장의 목소리를 반영한 정책 수립에 노력하고 있습니다. 정부의 이러한 접근 방식은 의료 교육 인프라의 지속 가능한 발전을 보장하고, 의료 서비스의 질을 유지하며, 국민의 건강과 복지를 증진시키기 위한 것입니다.\n",
      "\n",
      "정부는 의료 교육 인프라 확충과 질적 개선을 위한 장기적인 비전과 함께, 교육 내용의 개선, 교육 방법의 혁신, 그리고 실습 환경의 질적 향상을 포괄적으로 고려하고 있습니다. 이러한 정책과 계획을 통해 의료 교육 인프라의 질적 개선과 확충이 이루어지며, 이는 궁극적으로 국민의 건강과 복지 향상에 기여할 것입니다. 따라서 정부의 의료 교육 인프라 확충 계획은 충분히 구체적이며, 현실적으로 실현 가능한 방안을 포함하고 있습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Government(대한민국 정부)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n",
      "(Doctor Union(의사협회)): 이미 대화가 종료되었습니다.\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "max_iters = 30  # 최대 반복 횟수를 6으로 설정합니다.\n",
    "n = 0  # 반복 횟수를 추적하는 변수를 0으로 초기화합니다.\n",
    "\n",
    "# DialogueSimulator 객체를 생성하고, agents와 select_next_speaker 함수를 전달합니다.\n",
    "simulator = DialogueSimulator(\n",
    "    agents=agents_with_search, selection_function=select_next_speaker\n",
    ")\n",
    "\n",
    "# 시뮬레이터를 초기 상태로 리셋합니다.\n",
    "simulator.reset()\n",
    "\n",
    "# Moderator가 지정된 주제를 제시합니다.\n",
    "simulator.inject(\"Moderator\", specified_topic)\n",
    "\n",
    "# Moderator가 제시한 주제를 출력합니다.\n",
    "print(f\"(Moderator): {specified_topic}\")\n",
    "print(\"\\n\")\n",
    "\n",
    "while n < max_iters:  # 최대 반복 횟수까지 반복합니다.\n",
    "    name, message = (\n",
    "        simulator.step()\n",
    "    )  # 시뮬레이터의 다음 단계를 실행하고 발언자와 메시지를 받아옵니다.\n",
    "    print(f\"({name}): {message}\")  # 발언자와 메시지를 출력합니다.\n",
    "    print(\"\\n\")\n",
    "    n += 1  # 반복 횟수를 1 증가시킵니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2f1d160c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
